﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Reflection;
using System.ComponentModel;
using System.Collections;

namespace UnrealConverterLogic
{
    public partial class UnrealConverter
    {
        private static List<Ratio> Ratios = new List<Ratio>();
        private static Ratio UsedRatio = new Ratio("Default", 1, 1, 0.01);
        public static void SwitchMainInputsToRatios(object sender, EventArgs e)
        {
            rbConversion.TabStop = false;
            rbRatios.TabStop = false;
            if (rbRatios.Checked)
            {
                StopListeningForConversionChanges();
                SetInputsToRatio();
                SetListenersForRatioEdits();
            }
        }

        public static void SetInputsToRatio()
        {
            bEditingRatios = true;

            tbInputSetName.Text = UsedRatio.SetName;
            tbInputUU.Text = UsedRatio.UnrealUnits.ToString();
            tbInputMaxUnits.Text = UsedRatio.MaxUnits.ToString();
            tbInputMeters.Text = UsedRatio.Meters.ToString();
        }
        public static void StopListeningForConversionChanges()
        {
            bConverting = false;
            tbInputSetName.TextChanged -= UnrealConverter.OnMathFieldChangedEventHandler;
            tbInputSetName.KeyDown -= UnrealConverter.OnInputSetNameKeyPress;

            tbInputUU.TextChanged -= UnrealConverter.OnMathFieldChangedEventHandler;
            tbInputUU.KeyDown -= UnrealConverter.OnInputSetNameKeyPress;

            tbInputMaxUnits.TextChanged -= UnrealConverter.OnMathFieldChangedEventHandler;
            tbInputMaxUnits.KeyDown -= UnrealConverter.OnInputSetNameKeyPress;

            tbInputMeters.TextChanged -= UnrealConverter.OnMathFieldChangedEventHandler;
            tbInputMeters.KeyDown -= UnrealConverter.OnInputSetNameKeyPress;

            tbInputUU.LostFocus -= UnrealConverter.OnMathFieldFocusLost;
            tbInputUU.GotFocus -= UnrealConverter.OnMathFieldFocusGained;

            tbInputMaxUnits.LostFocus -= UnrealConverter.OnMathFieldFocusLost;
            tbInputMaxUnits.GotFocus -= UnrealConverter.OnMathFieldFocusGained;

            tbInputMeters.LostFocus -= UnrealConverter.OnMathFieldFocusLost;
            tbInputMeters.GotFocus -= UnrealConverter.OnMathFieldFocusGained;
        }
        public static void SetListenersForRatioEdits()
        {
            bEditingRatios = true;
            tbInputSetName.TextChanged += UnrealConverter.OnRatioFieldChangedEventHandler;
            tbInputSetName.KeyDown += UnrealConverter.OnRatioFieldEnterPressed;

            tbInputUU.TextChanged += UnrealConverter.OnRatioFieldChangedEventHandler;
            tbInputUU.KeyDown += UnrealConverter.OnRatioFieldEnterPressed;

            tbInputMaxUnits.TextChanged += UnrealConverter.OnRatioFieldChangedEventHandler;
            tbInputMaxUnits.KeyDown += UnrealConverter.OnRatioFieldEnterPressed;

            tbInputMeters.TextChanged += UnrealConverter.OnRatioFieldChangedEventHandler;
            tbInputMeters.KeyDown += UnrealConverter.OnRatioFieldEnterPressed;

        }

        public static void UpdateScaleIncrease()
        {
            lDynamicScaleIncrease.Text = fMaxtoUU.ToString() + " x PU = UU";
        }

        public static void OnRatioFieldEnterPressed(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                Ratio ratio = new Ratio(UsedRatio.SetName, UsedRatio.UnrealUnits, UsedRatio.MaxUnits, UsedRatio.Meters);
                AddRatio(ratio);
            }
        }
        public static void RatioChanged()
        {
            switch (LastFocusedInputField)
            {
                case InputField.UNREAL:
                    UpdateMathFields(Units.UNREAL_UNITS);
                    break;
                case InputField.MAX:
                    UpdateMathFields(Units.MAX_UNITS);
                    break;
                case InputField.METERS:
                    UpdateMathFields(Units.METERS);
                    break;
            }
            UpdateGridArrays();
            UpdateScaleIncrease();
        }
        public static void OnRatioListItemSelected(object sender, EventArgs e)
        {
            if (lvRatioList.SelectedItem != null)
            {
            UsedRatio = InterpretRatio(lvRatioList.SelectedItem.ToString());
            UpdateRatios();
            RatioChanged();
                }
        }
        public static void OnRatioListDeletePressed(object sender, KeyEventArgs e)
        {
            if (lvRatioList.SelectedItem != null)
            {
                if (e.KeyCode == Keys.Delete)
                {
                    Ratio ratio = new Ratio(lvRatioList.SelectedItem.ToString());
                    RemoveRatio(ratio);
                }
            }
        }
        public static void OnRatioFieldChangedEventHandler(object sender, EventArgs e)
        {
            string ControlName = ((TextBox)sender).Name;
            if (ControlName.Contains("SetName") && tbInputUU.Text != "")
            {
                UsedRatio.SetName = tbInputSetName.Text;
            }
            if ( ControlName.Contains("UU") && tbInputUU.Text != "")
            {
                UsedRatio.UnrealUnits = Double.Parse(tbInputUU.Text);
            }
            if (ControlName.Contains("Max") && tbInputMaxUnits.Text != "")
            {
                UsedRatio.MaxUnits = Double.Parse(tbInputMaxUnits.Text);
            }
            if (ControlName.Contains("Meters") && tbInputMeters.Text != "")
            {
                UsedRatio.Meters = Double.Parse(tbInputMeters.Text);
            }

            UpdateRatios();
            switch (LastFocusedInputField)
            {
                case InputField.UNREAL:
                    CurrentConversion.MaxUnits  = CurrentConversion.UnrealUnits * fMaxtoUU;
                    CurrentConversion.Meters    = CurrentConversion.UnrealUnits * fMeterstoUU;
                    break;
                case InputField.MAX:
                    CurrentConversion.Meters        = CurrentConversion.MaxUnits * fMeterstoMax;
                    CurrentConversion.UnrealUnits   = CurrentConversion.MaxUnits * fUUtoMax;
                    break;
                case InputField.METERS:
                    CurrentConversion.MaxUnits = CurrentConversion.Meters * fMaxtoMeters;
                    CurrentConversion.UnrealUnits = CurrentConversion.Meters * fUUtoMeters;
                    break;
            }
            UpdateGridArrays();
            UpdateScaleIncrease();
        }
        public static void UpdateRatios()
        {
            double UU       = UsedRatio.UnrealUnits;
            double Max      = UsedRatio.MaxUnits;
            double Meters   = UsedRatio.Meters;

            fUUtoMax = Max / UU;
            fUUtoMeters = Meters / UU;
            fMaxtoUU = 1 / fUUtoMax;
            fMaxtoMeters = Meters / Max;
            fMeterstoUU = 1 / fUUtoMeters;
            fMeterstoMax = 1 / fMaxtoMeters;
            lRatio.Text = UsedRatio.ToString().Substring(UsedRatio.ToString().LastIndexOf("]") + 1);
        }

        public static void AddRatio(Ratio ratio)
        {
            /* Check for duplicates */
            if (!InterpretRatios().Exists(r => r == ratio))
            {
                Ratios.Add(ratio);
                lvRatioList.Items.Add(ratio.ToString());
            }
        }
        public static void RemoveRatio(Ratio ratio)
        {
            Ratio defaultRatio = new Ratio("Default", 1, 1, 0.01);
            /* You cannot delete the defaults */
            if (ratio == defaultRatio) return;

            Ratios.Remove(ratio);
            lvRatioList.Items.Remove(lvRatioList.SelectedItem);
        }

        public static Ratio InterpretRatio(string ratio)
        {
            string SetName = Regex.Match(ratio, @"(?<=\[)[A-Za-z0-9]*").Value;
            double UnrealUnits = Double.Parse(Regex.Match(ratio, "[0-9.]+(?= UU)").Value);
            double MaxUnits = Double.Parse(Regex.Match(ratio, "[0-9.]+(?= PU)").Value);
            double Meters = Double.Parse(Regex.Match(ratio, "[0-9.]+(?= M)").Value);

            Ratio r = new Ratio(SetName, UnrealUnits, MaxUnits, Meters);

            return r;
        }
        public static List<Ratio> InterpretRatios()
        {
            List<Ratio> ratios = new List<Ratio>();
            foreach(Object item in lvRatioList.Items)
            {
                Ratio r = InterpretRatio(item.ToString());

                ratios.Add(r);
            }
            return ratios;
        }
    }
    public partial class Ratio : IEnumerable, IEquatable<Ratio>
    {
        public string SetName { get; set; }
        public double UnrealUnits { get; set; }
        public double MaxUnits { get; set; }
        public double Meters { get; set; }

        public Ratio(string str)
        {
            this.SetName = Regex.Match(str.ToString(), @"(?<=\[)[A-Za-z0-9]*").Value;
            this.UnrealUnits = Double.Parse(Regex.Match(str.ToString(), "[0-9.]+(?= UU)").Value);
            this.MaxUnits = Double.Parse(Regex.Match(str.ToString(), "[0-9.]+(?= PU)").Value);
            this.Meters = Double.Parse(Regex.Match(str.ToString(), "[0-9.]+(?= M)").Value);
        }
        public Ratio(string SetName, double UnrealUnits, double MaxUnits, double Meters)
        {
            this.SetName = SetName;
            this.UnrealUnits = UnrealUnits;
            this.MaxUnits = MaxUnits;
            this.Meters = Meters;
        }

        public string ToString()
        {
            return "[" + SetName + "] " + UnrealUnits + " UU : " + MaxUnits + " PU : " + Meters + " M";
        }
        public string[] ToStringArray()
        {
            return new string[]
            {
                SetName,
                UnrealUnits.ToString(),
                MaxUnits.ToString(),
                Meters.ToString()
            };
        }
    }
    public partial class Ratio : IEquatable<Ratio>
    {
        public static bool operator==(Ratio r1, Ratio r2)
        {
            return
                (
                    r1.SetName == r2.SetName &&
                    r1.UnrealUnits == r2.UnrealUnits &&
                    r1.MaxUnits == r2.MaxUnits &&
                    r1.Meters == r2.Meters
                );
        }
        public static bool operator !=(Ratio r1, Ratio r2)
        {
            if (r1 == null)
            {
                if (r2 == null) return true;
                else return false;
            }
            else
            {
                if (r2 == null) return false;
            }
            return
                !(
                    r1.SetName == r2.SetName &&
                    r1.UnrealUnits == r2.UnrealUnits &&
                    r1.MaxUnits == r2.MaxUnits &&
                    r1.Meters == r2.Meters
                );
        }

        public bool Equals(Ratio other)
        {
            return  
                    this.SetName == other.SetName           &&
                    this.UnrealUnits == other.UnrealUnits   &&
                    this.MaxUnits == other.MaxUnits         &&
                    this.Meters == other.Meters             
            ;
        }
    }
    public partial class Ratio : IEnumerable
    {
        private Ratio[] _ratio;

        public Ratio(Ratio[] rArray)
        {
            _ratio = new Ratio[rArray.Length];

            _ratio = new Ratio[rArray.Length];

            for (int i = 0; i < rArray.Length; i++)
            {
                _ratio[i] = rArray[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }

        public RatioEnum GetEnumerator()
        {
            return new RatioEnum(_ratio);
        }
    }
    public class RatioEnum : IEnumerator
    {
        public Ratio[] _ratio;

        // Enumerators are positioned before the first element 
        // until the first MoveNext() call. 
        int position = -1;

        public RatioEnum(Ratio[] list)
        {
            _ratio = list;
        }

        public bool MoveNext()
        {
            position++;
            return (position < _ratio.Length);
        }

        public void Reset()
        {
            position = -1;
        }

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public Ratio Current
        {
            get
            {
                try
                {
                    return _ratio[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }

}
